/**
 * 
 */
package info.niwota.web.apache;

import info.niwota.web.Debug;
import info.niwota.web.proxy.HttpWorkerThread;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import x.org.apache.http.HttpRequestInterceptor;
import x.org.apache.http.HttpResponseInterceptor;
import x.org.apache.http.impl.DefaultConnectionReuseStrategy;
import x.org.apache.http.impl.DefaultHttpResponseFactory;
import x.org.apache.http.params.CoreConnectionPNames;
import x.org.apache.http.params.CoreProtocolPNames;
import x.org.apache.http.params.HttpParams;
import x.org.apache.http.params.SyncBasicHttpParams;
import x.org.apache.http.protocol.HttpContext;
import x.org.apache.http.protocol.HttpProcessor;
import x.org.apache.http.protocol.HttpRequestHandlerResolver;
import x.org.apache.http.protocol.HttpService;
import x.org.apache.http.protocol.ImmutableHttpProcessor;
import x.org.apache.http.protocol.ResponseConnControl;
import x.org.apache.http.protocol.ResponseContent;
import x.org.apache.http.protocol.ResponseDate;
import x.org.apache.http.protocol.ResponseServer;
import x.org.apache.http.protocol.SyncBasicHttpContext;
import android.content.Context;
import android.util.Log;

/**
 * @author qiangli
 * 
 */
public class Connector extends Thread {
	private static final HttpContext webcontext = new SyncBasicHttpContext(null);

	private static final String TAG = "Connector";

	private static final String HOTWEB_SERVER = "Blueweb 2.0";

	private static final int BUFSIZE = 8 * 1024;

	private int timeout = 60000; // 5000;

	private ServerSocket ss;

	private HttpParams params;

	private HttpService httpService;

	private boolean running = false;

	private ExecutorService pool = null;

	public Connector(ServerSocket ss) {
		this.ss = ss;
	}

	public void init(Context ctx) throws Exception {
		configure(ctx);
	}

	@Override
	public String toString() {
		return "Connector [ss=" + ss + "]";
	}

	private void configure(Context ctx) {
		this.params = new SyncBasicHttpParams();
		this.params
				.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, timeout)
				.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE,
						BUFSIZE)
				.setBooleanParameter(
						CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
				.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
				.setParameter(CoreProtocolPNames.ORIGIN_SERVER, HOTWEB_SERVER);
		
		//
		HttpProcessor httpproc = new ImmutableHttpProcessor(
				new HttpRequestInterceptor[] {new ProxyRequestInterceptor(),
						new SessionRequestInterceptor(),
						new RequestParametersInterceptor(ctx) },
						
				new HttpResponseInterceptor[] { new ResponseDate(),
						new ResponseServer(), new ResponseContent(),
						new ResponseConnControl(),
						new SessionResponseInterceptor() });
		//
		HttpRequestHandlerResolver reqistry = new HttpHandlerRegistry(ctx);
		//reqistry.register("*", handler);

		//
		this.httpService = new WebHttpService(httpproc,
				new DefaultConnectionReuseStrategy(),
				new DefaultHttpResponseFactory(), reqistry, this.params);
	}

	@Override
	public void run() {
		if (Debug.DEBUG) {
			try {
				Log.d(TAG,
						"Listening on " + ss.getInetAddress() + ":"
								+ ss.getLocalPort() + " reuse: "
								+ ss.getReuseAddress());
			} catch (SocketException e) {
				e.printStackTrace();
			}
		}
		
		pool = Executors.newCachedThreadPool();
		//
		running = true;
		//
		service();
		//
		if (Debug.DEBUG) {
			Log.d(TAG, "*********** exiting " + this);
		}
		//
		if (ss != null) {
			try {
				ss.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		//
		running = false;
		
		//
		pool.shutdownNow();
		pool = null;
	}

	private void service() {
		while (running) {
			try {
				if (Thread.currentThread().isInterrupted()) {
					break;
				}
				
				if (ss.isClosed()) {
					break;
				}
				
				if (Debug.DEBUG) {
					Log.d(TAG, this + " accepting connection: " + ss);
				}
				
				Socket s = ss.accept();
				
				if (Debug.DEBUG) {
					Log.d(TAG, this + " accepted connection: " + s);
				}
	
				//
				Runnable r = new HttpWorkerThread(webcontext, this.httpService, this.params, s);
				
				pool.execute(r);
				
			} catch (Throwable e) {
				e.printStackTrace();
				//
				if (Debug.DEBUG) {
					Log.d(TAG, ss + ": " + e);
				}
			}
		}
	}

	public void stopRunning() {
		running = false;
	}
}
